<?php
/**
 * +--------------------------------------------------------------
 * | eLuoPHP
 * +--------------------------------------------------------------
 * | Copyright (c) 2013-2020 http://qxwoo.cn All rights reserved.
 * +--------------------------------------------------------------
 * | y139w <y139w@163.com>
 * +--------------------------------------------------------------
 * | 2020/2/5 23:11 Yier
 * +--------------------------------------------------------------
 * | Upload.php
 * +--------------------------------------------------------------
 */

namespace luo\upload;


use luo\Exception;

class Upload
{

    private $key = '';

    private $absolutePath = '';

    private $relativePath = '';

    private $saveAbsolutePath = '';

    private $saveRelativePath = '';

    private $attachmentDirectory = '';

    private $file = null;

    private $name = null;

    private $ext = null;

    private $type = null;

    private $tmp = null;

    private $error = null;

    private $size = 0;

    private $sizeLimit = 0;

    private $allowExt = false;

    private $extDirectory = 'image';

    private $dateDirectory = '';

    private $isDateDirectory = true;

    private $finalSavePath = '';

    private $finalSaveRelativePath = '';

    private $originalFileName = false;

    private $saveFileName = '';

    private $errorMsg = 'waiting upload';

    private $success = false;

    public function __construct($file = null, $attached = 'uploads/')
    {

        $this->key = $file['key'];
        $this->setRootPath();
        $this->setSavePath();
        $this->setAttached($attached);
        $this->file = $file;
        $this->name = $file['name'];
        $nameArray = explode('.', $file['name']);
        $this->ext = $nameArray[count($nameArray) - 1];
        $this->type = $file['type'];
        $this->tmp = $file['tmp_name'];
        $this->error = $file['error'];
        $this->size = $file['size'];
        $this->dateDirectory = date('Ymd');

    }

    public function setRootPath($path = ''){
        if(empty($path)){
            $path = dirname($_SERVER['SCRIPT_FILENAME']) . '/';
        }
        $this->absolutePath = $path;
        return $this;
    }

    public function setSavePath($trajectory = ''){
        if(empty($trajectory)){
            $scriptFilenameDirectory = dirname($_SERVER['SCRIPT_FILENAME']);
            $root = str_replace($_SERVER['DOCUMENT_ROOT'], '', $scriptFilenameDirectory);
            $trajectory = ($root === '/') ? '/' : $root . '/';
        }
        $this->relativePath = $trajectory;
        return $this;
    }

    public function setAttached($attached = 'uploads/'){
        $this->attachmentDirectory = $attached;
        $this->saveAbsolutePath = realpath($this->absolutePath . $attached) . '/';
        $this->saveRelativePath = $this->relativePath . $attached;
        return $this;
    }

    public function setSizeLimit($limit = 0){

        $this->sizeLimit = $limit > 0 ? $limit : 2 * 1024 * 1024;
        return $this;

    }

    public function setExtDirectory($ext = null){

        $this->extDirectory = $ext;
        return $this;

    }

    public function setAllowExt($ext = false){

        $this->allowExt = $ext;
        return $this;

    }

    /**
     * @return $this
     */
    public function save(){

        // throw upload error exception
        if($this->error != 0){
            $this->errorMsg = $this->error($this->error);
        }
        // final save directory handle
        $this->saveDirectoryHandle();
        if(!$this->checkWritable()){
            $this->errorMsg = 'directory not write';
        }
        if(!$this->checkUploadedFile()){
            $this->errorMsg = 'file not uploaded';
        }
        if(!$this->checkSize()){
            $this->errorMsg = 'file size is too large';
        }
        $this->saveFileName = $this->fileNameAndExtensionHandle();
        $saveFileName = implode('/', [$this->finalSavePath, $this->saveFileName]);
        if(!move_uploaded_file($this->tmp, $saveFileName)){
            $this->errorMsg = 'file upload failed';
        }else{
            $this->success = true;
            $this->errorMsg = 'success';
        }
        return $this;

    }

    /**
     * @return string
     */
    public function getErrorMessage(){
        return $this->errorMsg;
    }

    /**
     * @return bool
     */
    public function success(){

        return $this->success;

    }

    /**
     * @return string
     */
    public function getSaveFile(){

        $saveFileArray = [
            $this->finalSaveRelativePath,
            $this->saveFileName
        ];
        return implode('/', $saveFileArray);

    }

    /**
     * @return mixed|string|null
     */
    private function fileNameAndExtensionHandle(){

        $nameExt = strtolower($this->ext);
        $fileSaveName = implode('.', [uniqid(), $nameExt]);
        if($this->originalFileName){
            $fileSaveName = $this->name;
            if(is_string($this->originalFileName)){
                $fileSaveName = $this->originalFileName;
            }
        }
        return $fileSaveName;

    }

    /**
     * @return string
     */
    private function saveDirectoryHandle(){

        $saveAbsolutePath = trim($this->saveAbsolutePath, '/');
        $dateDirectory = '';
        if($this->isDateDirectory){
            $dateDirectory = trim($this->dateDirectory, '/');
        }
        $attachmentDirectory = trim($this->attachmentDirectory, '/');
        $this->finalSaveRelativePath = implode('/', [$attachmentDirectory, $this->extDirectory, $dateDirectory]);
        $finalSavePath = implode('/', [$saveAbsolutePath, $this->extDirectory, $dateDirectory]);
        if(!is_dir($finalSavePath)){
            mkdir($finalSavePath, 0755, true);
        }
        $this->finalSavePath = $finalSavePath;
        return $this;

    }

    /**
     * @return bool
     */
    private function checkSize(){

        if($this->size < $this->sizeLimit){
            return true;
        }
        return false;

    }

    /**
     * @return bool
     */
    private function checkUploadedFile(){

        if(is_uploaded_file($this->tmp)){
            return true;
        }
        return false;

    }

    /**
     * @return bool
     */
    private function checkWritable(){

        if(is_writable($this->finalSavePath)){
            return true;
        }
        return false;

    }

    /**
     * @param int $error
     * @return mixed|string
     */
    private function error($error = 0){

        $error = $error == $this->error ? $error : $this->error;
        // error info array
        $errorArray = [
            UPLOAD_ERR_OK         => 'There is no error, the file uploaded with success.',
            UPLOAD_ERR_INI_SIZE   => 'The uploaded file exceeds the upload_max_filesize directive in php.ini.',
            UPLOAD_ERR_FORM_SIZE  => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
            UPLOAD_ERR_PARTIAL    => 'The uploaded file was only partially uploaded.',
            UPLOAD_ERR_NO_FILE    => 'No file was uploaded.',
            UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder. Introduced in PHP 5.0.3.',
            UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk. Introduced in PHP 5.1.0.',
            UPLOAD_ERR_EXTENSION  => 'A PHP extension stopped the file upload. PHP does not provide a way to ascertain which extension caused the file upload to stop; examining the list of loaded extensions with phpinfo() may help. Introduced in PHP 5.2.0.',
        ];
        //$returnError = isset($errorArray[$error]) ? $errorArray[$error] : 'unknown error';
        // get error info
        return isset($errorArray[$error]) ? $errorArray[$error] : 'unknown error';

    }

}